using System;
using System.Data;
using System.Globalization;
using System.Xml;


namespace FinlogiK.FxCop.Statistics
{
	internal sealed class StatisticsPersister
	{
		#region Fields

		private readonly string _connectionString;

		#endregion

		#region Constructors

		internal StatisticsPersister(string connectionString)
		{
			if (connectionString == null) throw new ArgumentNullException("connectionString");

			this._connectionString = connectionString;
		}

		#endregion

		#region Properties

		#endregion

		#region Methods

		private IDbConnection OpenConnection()
		{
			return DbUtility.OpenConnection(this._connectionString);
		}

		internal int SaveSummaryStatistics(int projectId, DateTime analysisTimestamp, ViolationStatisticType statisticType, XmlDocument data)
		{
			using (IDbConnection connection = this.OpenConnection())
			using (IDbTransaction transaction = connection.BeginTransaction())
			{
				int analysisId = StatisticsPersister.CreateAnalysisRun(transaction, projectId, analysisTimestamp, statisticType);

				using (IDbCommand command = connection.CreateCommand())
				{
					command.CommandText = "spAddViolationStatistic";
					command.CommandType = CommandType.StoredProcedure;
					command.Transaction = transaction;

					DbUtility.AddParameter(command, "AnalysisID", DbType.Int32, analysisId);
					DbUtility.AddParameter(command, "RuleCategory", DbType.String, 100, null);
					DbUtility.AddParameter(command, "RuleName", DbType.String, 200, null);
					DbUtility.AddParameter(command, "ViolationCount", DbType.Int32, null);

					foreach (XmlElement element in data.GetElementsByTagName("Rule"))
					{
						XmlAttributeCollection attributes = element.Attributes;

						StatisticsPersister.SaveViolationStatistic(command,
							attributes[0].Value, // category name
							attributes[1].Value, // rule name
							int.Parse(attributes[2].Value, CultureInfo.InvariantCulture) // violation count
							);
					}
				}

				transaction.Commit();
				return analysisId;
			}
		}

		private static int CreateAnalysisRun(IDbTransaction transaction, int projectId, DateTime analysisTimestamp, ViolationStatisticType statisticType)
		{
			using (IDbCommand command = transaction.Connection.CreateCommand())
			{
				command.CommandText = "spCreateAnalysisRun";
				command.CommandType = CommandType.StoredProcedure;
				command.Transaction = transaction;

				DbUtility.AddParameter(command, "ProjectID", DbType.Int32, projectId);
				DbUtility.AddParameter(command, "AnalysisTimestamp", DbType.DateTime, analysisTimestamp);
				DbUtility.AddParameter(command, "StatisticTypeID", DbType.Int32, statisticType);
				IDbDataParameter idParameter = DbUtility.AddParameter(command, "AnalysisID", DbType.Int32, ParameterDirection.Output, null);

				DbUtility.PrepareParameters(command);
				command.ExecuteNonQuery();

				return (int)idParameter.Value;
			}
		}

		private static void SaveViolationStatistic(IDbCommand command, string categoryName, string ruleName, int violationCount)
		{
			DbUtility.SetParameterValue(command.Parameters[1], categoryName);
			DbUtility.SetParameterValue(command.Parameters[2], ruleName);
			DbUtility.SetParameterValue(command.Parameters[3], violationCount);

			DbUtility.PrepareParameters(command);
			command.ExecuteNonQuery();
		}

		internal void RemoveAllStatistics(int projectId, DateTime analysisTimestamp)
		{
			using (IDbConnection connection = this.OpenConnection())
			using (IDbCommand command = connection.CreateCommand())
			{
				command.CommandText = "spDeleteStatistics";
				command.CommandType = CommandType.StoredProcedure;

				DbUtility.AddParameter(command, "ProjectID", DbType.Int32, projectId);
				DbUtility.AddParameter(command, "AnalysisTimestamp", DbType.DateTime, analysisTimestamp);

				command.ExecuteNonQuery();
			}
		}

		#endregion
	}
}